package cn.flkj.gmca.ca.gmca;

import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.*;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.X509KeyUsage;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.encoders.Base64;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.security.*;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Date;

/**
 * @author : 州长在手 2021/3/2 下午3:13
 */
public class GmCa {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * Parse X509 PEM to Certificate
     *
     * @param cert PEM 不需要掐头去尾
     * @return
     */
    public static X509Certificate ParseX509PEMtoCertificate(String cert) {
        byte[] cer = cert.getBytes();
        CertificateFactory certificateFactory = null;
        try {
            certificateFactory = CertificateFactory.getInstance("X509", "BC");
        } catch (CertificateException | NoSuchProviderException e) {
            e.printStackTrace();
        }
        InputStream inputStream = new ByteArrayInputStream(cer);
        try {
            X509Certificate caRootCert = (X509Certificate) certificateFactory.generateCertificate(inputStream);
            return caRootCert;
        } catch (CertificateException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * @param sk PEM  直接丢进去
     * @return
     */
    public static PrivateKey ParseSKtoPrivateKey(String sk) {
        int start = sk.indexOf("-----", 5);
        int end = sk.indexOf("-----", start + 5);
        byte[] privSk = Base64.decode(sk.substring(start + 5, end));
        KeyFactory keyFactory = null;
        try {
            keyFactory = KeyFactory.getInstance("EC");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privSk);
        PrivateKey caPrivateKey = null;
        try {
            caPrivateKey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
            return null;
        }
        return caPrivateKey;
    }

    /**
     * 证书续签,这里是加载原有证书读取保证了公钥和序列号不变
     * 使用BC库这样续签的代码，fabric 是能够识别的
     * @param caCert
     * @param caPrivateKey
     * @param subCert
     */
    public static X509Certificate certRenewal(X509Certificate caCert, PrivateKey caPrivateKey, X509Certificate subCert) {

            try {
                //       KeyPairGenerator g = KeyPairGenerator.getInstance("EC", "BC");
                //     g.initialize(new ECNamedCurveGenParameterSpec("sm2p256v1"));

                // 公钥算法
                //    String pubAlg = subCert.getPublicKey().getAlgorithm();
                // ca 签名器
                ContentSigner sigGen = new JcaContentSignerBuilder("SM3withSM2").setProvider("BC").build(caPrivateKey);

                X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(
                        caCert, // 这里用的构造函数不同，也可以是颁发者主题
                        subCert.getSerialNumber(),
                        new Date(System.currentTimeMillis()),// 颁发证书的时间这里是1年 * 10
                        new Date(System.currentTimeMillis() + 31536000000L * 10),
                        subCert.getSubjectX500Principal(), // 子主题
                        subCert.getPublicKey()
                );

                // ----------------------------------------------------------

                KeyUsage usage = new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyAgreement
                        | KeyUsage.dataEncipherment | KeyUsage.keyEncipherment);

                KeyPurposeId[] extendedKeyUsages =  new KeyPurposeId[] {KeyPurposeId.id_kp_clientAuth, KeyPurposeId.id_kp_serverAuth};

                JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();

                certGen.addExtension(Extension.authorityKeyIdentifier, false,
                        extUtils.createAuthorityKeyIdentifier(SubjectPublicKeyInfo.getInstance(caCert.getPublicKey().getEncoded())));

                // 基础约束,具有基本约束：证书颁发机构等于true的X509证书不能用作标识
                BasicConstraints  basicConstraints = new BasicConstraints(false);
                certGen.addExtension(Extension.basicConstraints, true, basicConstraints);
                // 关键用法
                certGen.addExtension(Extension.keyUsage, true, usage);

                ExtendedKeyUsage xku = new ExtendedKeyUsage(extendedKeyUsages);
                certGen.addExtension(Extension.extendedKeyUsage, false, xku);
                // ------------------------------------------------------------
                X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certGen.build(sigGen));

                cert.checkValidity(new Date());
                cert.verify(caCert.getPublicKey());
                ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
                CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
                cert = (X509Certificate) fact.generateCertificate(bIn);
                return cert;
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }

    }

    /**
     * 整理 Encode 的证书，每64一行，添加头和尾
     * @param cert
     * @return
     */
    public static String modifyCertToPem(X509Certificate cert){
        try {
            String content = Base64.toBase64String(cert.getEncoded());
            String START = "-----BEGIN CERTIFICATE-----\n";
            String END ="-----END CERTIFICATE-----";
            return START + modifyCertToPemHelper(content)+END;
        } catch (CertificateEncodingException e) {
            e.printStackTrace();
            return null;
        }
    }
    private static String modifyCertToPemHelper(String subContent){
        // 递归结束条件，就是不递归了
        if (subContent.length() == 0){ // 需要考虑证书是64的倍数问题
            return  subContent;
        }
        if (subContent.length() < 64){
            return  subContent+"\n";
        }

        String start64 = subContent.substring(0,64);
        String rest = subContent.substring(64);
        // 一定要相信递归函数返回的结果就是子问题需要的结果
        String modifyResult = modifyCertToPemHelper(rest);
        // 然后使用子问题的结果，返回上一层递归需要的结果
        return start64 +"\n" + modifyResult;
    }
}
